odhcpd: change "-c" cmd line arg to take a dir
authorDavid Härdeman <[email protected]>
Sat, 25 Oct 2025 13:32:06 +0000 (15:32 +0200)
committerÁlvaro Fernández Rojas <[email protected]>
Sat, 25 Oct 2025 15:31:54 +0000 (17:31 +0200)
After the TZ support was added, odhcpd now reads two cfg files
potentially. Instead of adding a separate switch for the system UCI
file, I've changed the "-c" argument to take a directory. This is more
future-proof and also serves as a preparation for the global DUID PR
(which will necessitate reading from the UCI "network" file as well).

While I was at it, I also made some tiny improvements to the
odhcpd_reload() function to avoid an unnecessary allocation.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/291
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/odhcpd.c
src/odhcpd.h

index 3e916ad4cdac1a43bea6d92fcdf7d4de7e17d002..7bff307060e5e0439d41238acb6c09d1725a8526 100644 (file)
@@ -41,14 +41,13 @@ struct config config = {
        .dhcp_hostsfile = NULL,
        .ra_piofolder = NULL,
        .ra_piofolder_fd = -1,
-       .uci_cfgfile = "dhcp",
+       .uci_cfgdir = NULL,
        .log_level = LOG_WARNING,
        .log_level_cmdline = false,
        .log_syslog = true,
 };
 
 struct sys_conf sys_conf = {
-       .uci_cfgfile = "system",
        .posix_tz = NULL, // "timezone"
        .posix_tz_len = 0,
        .tzdb_tz = NULL, // "zonename"
@@ -2226,16 +2225,27 @@ void odhcpd_reload(void)
 {
        struct uci_context *uci = uci_alloc_context();
        struct interface *master = NULL, *i, *tmp;
+       char *uci_dhcp_path = "dhcp";
+       char *uci_system_path = "system";
 
        if (!uci)
                return;
 
+       if (config.uci_cfgdir) {
+               size_t dlen = strlen(config.uci_cfgdir);
+
+               uci_dhcp_path = alloca(dlen + sizeof("/dhcp"));
+               sprintf(uci_dhcp_path, "%s/dhcp", config.uci_cfgdir);
+               uci_system_path = alloca(dlen + sizeof("/system"));
+               sprintf(uci_system_path, "%s/system", config.uci_cfgdir);
+       }
+
        vlist_update(&leases);
        avl_for_each_element(&interfaces, i, avl)
                clean_interface(i);
 
        struct uci_package *dhcp = NULL;
-       if (!uci_load(uci, config.uci_cfgfile, &dhcp)) {
+       if (!uci_load(uci, uci_dhcp_path, &dhcp)) {
                struct uci_element *e;
 
                /* 1. Global settings */
@@ -2268,9 +2278,10 @@ void odhcpd_reload(void)
                }
                ipv6_pxe_dump();
        }
+       uci_unload(uci, dhcp);
 
        struct uci_package *system = NULL;
-       if (!uci_load(uci, sys_conf.uci_cfgfile, &system) && config.enable_tz == true) {
+       if (config.enable_tz && !uci_load(uci, uci_system_path, &system)) {
                struct uci_element *e;
 
                /* 1. System settings */
@@ -2280,12 +2291,12 @@ void odhcpd_reload(void)
                                set_timezone_info_from_uci(s);
                }
        }
+       uci_unload(uci, system);
 
        if (config.dhcp_statefile) {
-               char *path = strdup(config.dhcp_statefile);
+               char *path = strdupa(config.dhcp_statefile);
 
                mkdir_p(dirname(path), 0755);
-               free(path);
        }
 
        if (config.ra_piofolder) {
@@ -2374,8 +2385,6 @@ void odhcpd_reload(void)
                        close_interface(i);
        }
 
-       uci_unload(uci, dhcp);
-       uci_unload(uci, system);
        uci_free_context(uci);
 }
 
index 0f314bf7a61fb75715efbc0fc8fe729f0fd9e0fa..38d96fdfe799908605906b2fd78e9d3c5e076e34 100644 (file)
@@ -91,7 +91,7 @@ static void print_usage(const char *app)
 #endif /* EXT_CER_ID */
               "\n"
               "\n"
-              "        -c <path>       Use an alternative configuration file\n"
+              "        -c <dir>        Read UCI configuration files from <dir>\n"
               "        -l <int>        Specify log level 0..7 (default %d)\n"
               "        -f              Log to stderr instead of syslog\n"
               "        -h              Print this help text and exit\n",
@@ -117,9 +117,23 @@ int main(int argc, char **argv)
        while ((opt = getopt(argc, argv, "c:l:fh")) != -1) {
                switch (opt) {
                case 'c':
-                       config.uci_cfgfile = realpath(optarg, NULL);
-                       fprintf(stderr, "Configuration will be read from %s\n", config.uci_cfgfile);
+                       struct stat sb;
+                       char *path;
+
+                       free(config.uci_cfgdir);
+                       config.uci_cfgdir = NULL;
+
+                       path = realpath(optarg, NULL);
+                       if (!path || stat(path, &sb) || !S_ISDIR(sb.st_mode)) {
+                               fprintf(stderr, "%s is not a directory, ignoring\n", optarg);
+                               free(path);
+                               break;
+                       }
+
+                       fprintf(stderr, "Configuration will be read from %s\n", path);
+                       config.uci_cfgdir = path;
                        break;
+
                case 'l':
                        config.log_level = (atoi(optarg) & LOG_PRIMASK);
                        config.log_level_cmdline = true;
index 693da99783035fbc5cdd8de40ec367adc4399c0b..f2e2e74ca1b9e896d1d1ab87d143d8e4e303a6df 100644 (file)
@@ -194,7 +194,7 @@ struct config {
        char *ra_piofolder;
        int ra_piofolder_fd;
 
-       char *uci_cfgfile;
+       char *uci_cfgdir;
        int log_level;
        bool log_level_cmdline;
        bool log_syslog;
@@ -203,7 +203,6 @@ struct config {
 struct sys_conf {
        uint8_t *posix_tz;
        size_t posix_tz_len;
-       char *uci_cfgfile;
        uint8_t *tzdb_tz;
        size_t tzdb_tz_len;
 };